package cc.eevee.jni.libdynamic;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@SuppressWarnings({ "WeakerAccess", "unused" })
public class Injector {

  public static <T> T invoke(Class<?> clazz, String methodName, Object... args) {
    return invoke(clazz, null, methodName, args);
  }

  public static <T> T invoke(Object object, String methodName, Object... args) {
    return invoke(object.getClass(), object, methodName, args);
  }

  @SuppressWarnings("unchecked")
  public static <T> T invoke(Class<?> clazz, Object object, String methodName,
      Object... methodArgs) {
    try {
      Method method = getMethod(clazz, methodName, methodArgs);
      method.setAccessible(true);
      Object result = method.invoke(object, methodArgs);
      return (result == null) ? null : (T)(result) ;
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    }
    return null;
  }

  public static Method getMethod(Class<?> clazz, String name, Object... args)
      throws NoSuchMethodException {
    try {
      return clazz.getDeclaredMethod(name, getTypes(true, args));
    } catch (NoSuchMethodException e) {
      return clazz.getDeclaredMethod(name, getTypes(false, args));
    }
  }

  public static Class<?>[] getTypes(boolean toPrimitive, Object... args) {
    final int length = args.length;
    Class<?>[] types = new Class<?>[length];
    for (int i = 0; i < length; i++) {
      Class<?> type = args[i].getClass();
      if (toPrimitive)
        type = toPrimitiveClass(type);
      types[i] = type;
    }
    return types;
  }

  public static Class<?> toPrimitiveClass(Class<?> clazz) {
    if (clazz.equals(Byte.class)) return byte.class;
    if (clazz.equals(Short.class)) return short.class;
    if (clazz.equals(Integer.class)) return int.class;
    if (clazz.equals(Long.class)) return long.class;
    if (clazz.equals(Character.class)) return char.class;
    if (clazz.equals(Float.class)) return float.class;
    if (clazz.equals(Double.class)) return double.class;
    if (clazz.equals(Boolean.class)) return boolean.class;
    if (clazz.equals(Void.class)) return void.class;
    return clazz;
  }

}
